package com.ZeroCarbon.method;

import cn.hutool.core.bean.BeanUtil;
import com.ZeroCarbon.config.FeignConfig;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Map;

/**
 * 远程调用类
 *
 * @since 2025-2-8
 */
public class RemoteClientCall {

    //客户组端口号映射
    private final static Map<String, Integer> clientPort;

    static {
        //初始化远程服务对应端口号
        clientPort = Map.ofEntries(
                Map.entry("login", 8081),
                Map.entry("user", 8082),
                Map.entry("forum", 8083),
                Map.entry("shop", 8084)
        );
    }

    @Getter
    public enum OperationType {
        GET_BASE_INFO("getBaseInfo"),
        GET_USERNAME("getUsername"),
        GET_AVATAR("getAvatarById");

        private final String value;

        OperationType(String value) {
            this.value = value;
        }
    }

    /**
     * 远程调用获取封装结果
     */
    @SuppressWarnings("unchecked")
    public static <T, R> T getClientResult(Class<R>[] targetClient, OperationType method, Class<T> targetType, Object[] args) {
        //方法名
        String methodValue = method.getValue();
        //url方法前缀
        String prefix = Arrays.stream(targetClient)
                .map(Class::getSimpleName)
                .map(name -> name.replaceFirst("Client$", ""))
                .map(String::toLowerCase)
                .findFirst()
                .orElse("");
        //url完整路径
        String serviceUrl = "http://localhost:" + clientPort.get(prefix) + "/";
        //使用Feign构建实际的目标对象
        R realTarget = FeignConfig.createClient(targetClient[0], serviceUrl);
        //创建代理对象
        R client = (R) Proxy.newProxyInstance(
                targetClient[0].getClassLoader(),
                targetClient,
                new ClientInvocationHandler(realTarget)
        );
        try {
            //获取目标方法对象
            Method targetMethod = client.getClass().getMethod(methodValue, getParamTypes(args));
            //执行目标方法，并转化成目标类型
            return BeanUtil.copyProperties(targetMethod.invoke(client, args), targetType);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取传入参数类型数组
     */
    private static Class<?>[] getParamTypes(Object[] args) {
        return Arrays.stream(args)
                .map(Object::getClass)
                .toArray(Class<?>[]::new);
    }

    /**
     * 代理类
     */
    @RequiredArgsConstructor
    static class ClientInvocationHandler implements InvocationHandler {

        private final Object targetObject;

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(targetObject, args);
        }
    }
}
